#ifndef RENDERER_H
#define RENDERER_H

//Define constants
#define     CAMERA_RADIUS       100.0
#define     ANIMATION_SPEED     3
#define     TIME_INCREMENT      0.05
#define     FRAG_QUADRATIC      "quadratic.frag"
#define     VERT_QUADRATIC      "quadratic.vert"
#define     FRAG_CUBIC          "cubic.frag"
#define     VERT_CUBIC          "cubic.vert"

//Define macros
#define     onMouseMove         mouseMoveEvent
#define     onMouseDown         mousePressEvent
#define     onMouseUp           mouseReleaseEvent
#define     onKeyDown           keyPressEvent
#define     onWheelScroll       wheelEvent
#define     onGLWindowResize    resizeGL
#define     initOpenGL          initializeGL

//Include OpenMesh
#include <vector>
#include "meshdef.h"
#include <GL/glew.h>

//Include Qt
#include <QtOpenGL/qgl.h>
#include <QFileDialog>
#include <QMouseEvent>
#include <QTimer>
#include <qmenu.h>

//Include Misc.
#include "definitions.h"
#include "vector_math.h"
#include "Graphic.h"
#include "QuadraticCurve.h"
#include "CubicCurve.h"
#include "Circle.h"

//Include C++ stdl
#include <string>
//#include <vector>
#include <list>

//Include OpenGL
#include <GL/glut.h>

using namespace std;
using namespace Qt;
using namespace vmath;

namespace VecTools
{
    enum VecTool {SELECT, PEN1, PEN2, CIRCLE};
};
using namespace VecTools;

namespace MouseEvents
{
    enum MouseEvent {DOWN, MOVE, UP};
};

using namespace MouseEvents;

/**
    Class:      Camera
    Purpose:    Camera that can be moved manually to any position or made to 
                circle around some specified radius.
*/
struct Camera
{
    double radius;  // radius of rotation
    double angle;   // rotate by this many degrees
    double height;  // place camera at this height

    vec3d eye;  // location of the camera
    vec3d at;   // where the camera is looking
    vec3d up;   // camera's orientation (up) vector

    void updatePosition()
    {
        // right-hand-rule rotation
        eye[0] = radius * cos((angle/180) * M_PI);
        eye[1] = height;
        eye[2] = radius * sin((angle/180) * M_PI);

        // height positioning
        at[0] = at[2] = 0.0;
        at[1] = 0.0;

        // up vector is static
        up[0] = 0.0;
        up[1] = 1.0;
        up[2] = 0.0;
    };
};

/**
    Class:      Renderer
    Purpose:    Sets up the OpenGL environment in a Qt GL window and facilitates 
                rendering tasks. Also captures input events.
*/
class Renderer: public QGLWidget
{	
    Q_OBJECT

public:

	Renderer	( QWidget* parent );
	~Renderer	( );

    void setCurrentTool(VecTool);

signals:

    // Empty

public slots:

    void onTimerTick();
    
protected:

    // OpenGL-related methods
    void initOpenGL();
    void paintGL();
    void positionOpenGLCamera();

    // Misc. initializations
    void initAnimation();
    void initCamera();
    void initAttributes();

    // Event handlers
    void onGLWindowResize   ( int w, int h );
	void onMouseDown	    ( QMouseEvent* );
	void onMouseUp	        ( QMouseEvent* );
	void onMouseMove		( QMouseEvent* );
	void onWheelScroll	    ( QWheelEvent* );
    void onKeyDown          ( QKeyEvent* );

private:

    // Mouse tracking
    int oldMouseX;
    int oldMouseY;

    // Camera
    Camera camera;

    // Animation
    double time;
    QTimer aniTimer;

    // State vars
    bool    newModel;
    VecTool currentTool;

    // Drawing-related
    list<Graphic*>  graphics; 
    Graphic*        currentGraphic;
    Vertex*         currentVertex;
    Vertex*         vertNearMouse;

    // Helper functions
    int     nearestPowerOf2     ( int n );
    GLuint  loadTexture         ( QString fname, int dWidth, int dHeight );
    vec3d   getRandomVector     ( bool obeyBounds, double randLimit );
    vec3d   getCanvasPoint      ( const vec3d& windowPoint);
    void    unprojectRay        ( const vec3d& windowPoint, vec3d& rayOrigin, vec3d& rayDirection );
    void    unprojectPoint      ( vec3d& windowPoint, const double distance );
    void    doHoverSelection    ( QMouseEvent* e );

    void    doSelectTool    ( MouseEvent eventType, QMouseEvent* mouseEvent );
    void    doPen1Tool      ( MouseEvent eventType, QMouseEvent* mouseEvent );
    void    doPen2Tool      ( MouseEvent eventType, QMouseEvent* mouseEvent );
    void    doCircleTool    ( MouseEvent eventType, QMouseEvent* mouseEvent );
};

#endif